package com.google.code.netcheckin.aprs;

import java.text.SimpleDateFormat;
import java.text.DecimalFormat;
import java.text.FieldPosition;
import java.text.ParseException;
import java.text.ParsePosition;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.TimeZone;

import com.google.code.netcheckin.io.Ascii;

public class AprsFormatter {

		/** A list of valid icon symbols. */
	public static final byte[] VALID_ICONS = new byte[] {
		Ascii.V_EXCLAMATION_MARK,
		Ascii.V_QUOTEDBL,
		Ascii.V_NUMBER_SIGN,
		Ascii.V_DOLLAR,
		Ascii.V_PERCENT,
		Ascii.V_AMPERSAND,
		Ascii.V_QUOTE,
		Ascii.V_LEFT_PARENTHESIS,
		Ascii.V_RIGHT_PARENTHESIS,
		Ascii.V_ASTERISK,
		Ascii.V_PLUS,
		Ascii.V_COMMA,
		Ascii.V_MINUS,
		Ascii.V_PERIOD,
		Ascii.V_SLASH,
		Ascii.V_0,
		Ascii.V_1,
		Ascii.V_2,
		Ascii.V_3,
		Ascii.V_4,
		Ascii.V_5,
		Ascii.V_6,
		Ascii.V_7,
		Ascii.V_8,
		Ascii.V_9,
		Ascii.V_COLON,
		Ascii.V_SEMI_COLON,
		Ascii.V_LESS_THAN,
		Ascii.V_EQUALS,
		Ascii.V_GREATER_THAN,
		Ascii.V_QUESTION,
		Ascii.V_AT,
		Ascii.V_A,
		Ascii.V_B,
		Ascii.V_C,
		Ascii.V_D,
		Ascii.V_E,
		Ascii.V_F,
		Ascii.V_G,
		Ascii.V_H,
		Ascii.V_I,
		Ascii.V_J,
		Ascii.V_K,
		Ascii.V_L,
		Ascii.V_M,
		Ascii.V_N,
		Ascii.V_O,
		Ascii.V_P,
		Ascii.V_Q,
		Ascii.V_R,
		Ascii.V_S,
		Ascii.V_T,
		Ascii.V_U,
		Ascii.V_V,
		Ascii.V_W,
		Ascii.V_X,
		Ascii.V_Y,
		Ascii.V_Z,
		Ascii.V_OPEN_BRACKET,
		Ascii.V_BACK_SLASH,
		Ascii.V_CLOSE_BRACKET,
		Ascii.V_CIRCUMFLEX,
		Ascii.V_UNDERSCORE,
		Ascii.V_BACK_QUOTE,
		Ascii.V_a,
		Ascii.V_b,
		Ascii.V_c,
		Ascii.V_d,
		Ascii.V_e,
		Ascii.V_f,
		Ascii.V_g,
		Ascii.V_h,
		Ascii.V_i,
		Ascii.V_j,
		Ascii.V_k,
		Ascii.V_l,
		Ascii.V_m,
		Ascii.V_n,
		Ascii.V_o,
		Ascii.V_p,
		Ascii.V_q,
		Ascii.V_r,
		Ascii.V_s,
		Ascii.V_t,
		Ascii.V_u,
		Ascii.V_v,
		Ascii.V_w,
		Ascii.V_x,
		Ascii.V_y,
		Ascii.V_z,
		Ascii.V_BRACELEFT,
		Ascii.V_BRACERIGHT,
	};

		/** A list of icon symbols that can be overlaid. */
	public static final byte[] OVERLAY_ICONS = new byte[] {
		Ascii.V_NUMBER_SIGN,
		Ascii.V_AMPERSAND,
		Ascii.V_0,
		Ascii.V_GREATER_THAN,
		Ascii.V_A,
		Ascii.V_W,
		Ascii.V_CIRCUMFLEX,
		Ascii.V_UNDERSCORE,
		Ascii.V_a,
		Ascii.V_c,
		Ascii.V_i,
		Ascii.V_n,
		Ascii.V_s,
		Ascii.V_u,
		Ascii.V_v,
	};

	protected Calendar zuluCalendar;
	protected DecimalFormat formatDublZero;
	protected DecimalFormat formatTreblZero;
	protected DecimalFormat formatCoordMinutes;
	protected SimpleDateFormat formatZuluDate;

	public AprsFormatter() {
		zuluCalendar = new GregorianCalendar(TimeZone.getTimeZone("GMT+0"));
		formatDublZero = new DecimalFormat("00");
		formatTreblZero = new DecimalFormat("000");
			// extra precision for truncation rather than rounding
		formatCoordMinutes = new DecimalFormat("00.0000");
		formatZuluDate = new SimpleDateFormat("ddHHmm");
		formatZuluDate.setTimeZone(TimeZone.getTimeZone("GMT+0"));
	}

		/** Format a float as 00.00. */
	public String formatCoordinateMinutes(float minutes) {
		return formatCoordinateMinutes(new Float(minutes).doubleValue());
	}

		/** Format a double as 00.00. */
	public String formatCoordinateMinutes(double minutes) {
		return formatCoordMinutes.format(minutes, new StringBuffer(7), 
			new FieldPosition(0)).delete(5, 8).toString();
	}

		/** Format an integer with DecimalFormat pattern "00". */

	public String formatDoubleZero(int number) {
		return formatDublZero.format(new Integer(number).longValue());
	}

		/** Format an integer with DecimalFormat pattern "000". */

	public String formatTripleZero(int number) {
		return formatTreblZero.format(new Integer(number).longValue());
	}

		/** Turn a system time value into an APRS time stamp. */
	public String formatAprsTimestamp(long timestamp) {
		StringBuffer answer = new StringBuffer(7);
		answer = formatZuluDate.format(new Date(timestamp), answer, new FieldPosition(0));
		answer.append('z');
		return answer.toString();
	}

		/**
		  *  Are table and symbol valid?
		  * @param icon - a two character value that contains
		  *  symbol table followed by symbol value.
		  *
		*/

	public static final boolean isValidIcon(String icon) {
		if (icon == null || icon.length() < 2) return false;
		byte[] bytes = icon.getBytes();
		return isValidIcon(bytes[0], bytes[1]);
	}

		/** Are table and symbol valid? */
	public static final boolean isValidIcon(byte table, byte symbol) {
		if ( ! isValidIconSymbol(symbol)) return false;
		if (table == Ascii.V_SLASH || table == Ascii.V_BACK_SLASH)
			return true;
		return isIconSymbolOverlayable(symbol);
	}

	public static final boolean isValidIconSymbol(byte symbol) {
		for (int idx = 0; idx < VALID_ICONS.length; idx ++) {
			if (symbol == VALID_ICONS[idx]) {
				return true;
			}
		}
		return false;
	}

	public static final boolean isIconSymbolOverlayable(byte symbol) {
		for (int idx = 0; idx < OVERLAY_ICONS.length; idx ++) {
			if (symbol == OVERLAY_ICONS[idx]) {
				return true;
			}
		}
		return false;
	}

		/** Integer between 0 and 90 inclusive. */
	public int parseCoordinateLatitudeDegrees(String string)
				throws ParseException {
		int answer = parseCoordinateLongitudeDegrees(string);
		if (answer > 90 || answer < 0) {
			throw(new ParseException("out of range", 0));
		}
		return answer;
	}
		
		/** Integer between 0 and 180 inclusive. */
	public int  parseCoordinateLongitudeDegrees(String string) throws ParseException {
		string = string.trim();
		int answer = Integer.parseInt(string);
		if (answer > 180 || answer < 0) {
			throw(new ParseException("out of range", 0));
		}
		return answer;
	}
	
		/** Double between 0 and 59.99999999999999 inclusive. */
	public double parseCoordinateMinutes(String string) 
						throws ParseException {
		string = string.trim();
		double answer = Double.parseDouble(string);
		if (answer < 0.0 || answer >= 60.0) {
			throw(new ParseException("out of range", 0));
		}
		return answer;
	}

}
